-- tiles and tileset

------------ the basics ------------

tile_size = 512 -- size of each tile in world units

-- tile names, ordered by priority (lowest to highest)
tile_name = { "gravel", "sand", "rocks", "coral_g", "coral_r", "coral_p", "border" }

-- build id reverse lookup table
tile_id = {}
for i, v in ipairs(tile_name) do
	tile_id[v] = i
end

------------ tileset, stores the loaded images ------------

tileset = {}

-- each entry in tileset is numbered as such:
-- 5 4 3
-- 6 1 2
-- 7 8 9
-- tile 1 is the solid tile, the others are blends into adjacent tiles

function make_tiles(dir)
	local r = {}
	for j, w in ipairs(love.filesystem.enumerate(dir)) do
		local fdir = dir .. '/' .. w
		if string.match(fdir, "[^/]*\.png$") then
			table.insert(r, love.graphics.newImage(fdir))
		end
	end
	
	assert(#r == 9) -- make sure we loaded nine tiles!!!
	
	tile_scale = tile_size / r[1]:getWidth() -- grab one dimension to define scaling
	
	return r
end

------------ actual tile matrix ------------

tile = {
	img = nil, -- base image
	kind = 0, -- texture identifier
	tint_r = 255, tint_g = 255, tint_b = 255, -- tile tint
	blends = {}, -- additional blend tiles on top of this tile
}

function tile:new(o)
	o = o or {}
	setmetatable(o, self)
	self.__index = self
	return o
end

function tile:init(pkind)
	local r = tile:new()
	r.kind = pkind
	r.img = tileset[pkind][1]
	r.blends = {}
	
	if r.kind == tile_id["gravel"] or r.kind == tile_id["rocks"] then
		local tint = 200
		r.tint_r = tint
		r.tint_g = tint
		r.tint_b = tint
	end
	
	return r
end

function tile:grabTex()
	if #self.blends == 0 then
		return {self.kind, self.tint_r, self.tint_g, self.tint_b}
	else
		local b = self.blends[#self.blends]
		return {b.kind, b.tint_r, b.tint_g, b.tint_b}
	end
end

function tile:draw(px, py)
	love.graphics.setColor(self.tint_r, self.tint_g, self.tint_b)
	love.graphics.draw(self.img, px, py, 0, tile_scale, tile_scale)
	
	for i, v in ipairs(self.blends) do
		love.graphics.setColor(v.tint_r, v.tint_g, v.tint_b)
		love.graphics.draw(v.img, px, py, 0, tile_scale, tile_scale)
	end
end

tile_world = {
	tiles = {}, -- references to each tile, stored row major (like in a file)
	offset = vec2:init(-256, 256), -- world offset for upper left corner of tile map
	width = 0, height = 0, -- dimensions (in tiles)
}

function tile_world:test()
	self.width = 7
	self.height = 7
	
	local indices = {
		1, 1, 1, 1, 1, 1, 1, 
		1, 1, 1, 1, 1, 1, 1, 
		1, 1, 2, 1, 3, 1, 4, 
		1, 1, 2, 1, 1, 1, 1, 
		1, 1, 2, 2, 2, 5, 1, 
		1, 1, 1, 1, 1, 1, 1, 
		1, 1, 1, 1, 1, 1, 1, 
	}
		
	for i, v in ipairs(indices) do
		table.insert(self.tiles, tile:init(v))
	end
end

function tile_world:load(file)
	local lookup = {
		x = tile_id["border"],
		["."] = tile_id["sand"],
		a = tile_id["coral_g"],
		b = tile_id["coral_r"],
		c = tile_id["coral_p"],
		g = tile_id["gravel"],
		r = tile_id["rocks"],
	}
	
	self.width = 0
	self.height = 0
	self.tiles = {}
	
	contents, size = love.filesystem.read(file)
	for s in string.gmatch(contents, ".") do
		if s == '\n' then
			self.height = self.height + 1
			self.width = 0
		elseif s == ' ' or s == '\t' or s == '\r' then
			-- empty case, just fall through
		else
			table.insert(self.tiles, tile:init(lookup[s]))
			self.width = self.width + 1
		end
	end
	
--	self.width = #self.tiles / self.height 
end

function tile_world:lookup(pos)
	local idx = function (x, y) return self.width * (y-1) + x end
	local p = pos - self.offset
	p.y = -p.y
	p = (1 / tile_size) * p
	
	local lx = math.floor(p.x) + 1
	local ly = math.floor(p.y) + 1
	local ix = idx(lx, ly)
	
	if lx > 0 and lx <= self.width and ly > 0 and ly <= self.height then
		return self.tiles[idx(lx, ly)]
	else
		return nil
	end
end

function tile_world:pushDir(pos)
	local idx = function (x, y) return self.width * (y-1) + x end
	local p = pos - self.offset
	p.y = -p.y
	p = (1 / tile_size) * p
	
	local lx = math.floor(p.x) + 1
	local ly = math.floor(p.y) + 1
	
	if lx > 0 and lx <= self.width and ly > 0 and ly <= self.height and self.tiles[idx(lx, ly)].kind == tile_id["border"] then
		local cx, cy = pos.x - (self.offset.x + (lx - 0.5) * tile_size), pos.y - (self.offset.y + -(ly - 0.5) * tile_size)
		
		if math.abs(cx) > math.abs(cy) then -- push in x
			if cx < 0 then
				return vec2:init(-1, 0)
			else
				return vec2:init(1, 0)
			end
		else -- push in y
			if cy < 0 then
				return vec2:init(0, -1)
			else
				return vec2:init(0, 1)
			end
		end
	else
		return vec2:new()
	end
end

function tile_world:fill_blends()
	local idx = function (x, y) return self.width * (y-1) + x end
	local t, s
	
	-- run through all neighbors, adding blends to yourself as necessary
	
	for x = 1, self.width do
		for y = 1, self.height do
			s = self.tiles[idx(x, y)]
			
			if x < self.width then
				t = self.tiles[idx(x+1, y)]
				if (s.kind < t.kind) then
					table.insert(s.blends, {img = tileset[t.kind][6], kind = t.kind, tint_r = t.tint_r, tint_g = t.tint_g, tint_b = t.tint_b})
				end
			end
			if x < self.width and y > 1 then
				t = self.tiles[idx(x+1, y-1)]
				if (s.kind < t.kind) then
					table.insert(s.blends, {img = tileset[t.kind][7], kind = t.kind, tint_r = t.tint_r, tint_g = t.tint_g, tint_b = t.tint_b})
				end
			end
			if y > 1 then
				t = self.tiles[idx(x, y-1)]
				if (s.kind < t.kind) then
					table.insert(s.blends, {img = tileset[t.kind][8], kind = t.kind, tint_r = t.tint_r, tint_g = t.tint_g, tint_b = t.tint_b})
				end
			end
			if x > 1 and y > 1 then
				t = self.tiles[idx(x-1, y-1)]
				if (s.kind < t.kind) then
					table.insert(s.blends, {img = tileset[t.kind][9], kind = t.kind, tint_r = t.tint_r, tint_g = t.tint_g, tint_b = t.tint_b})
				end
			end
			if x > 1 then
				t = self.tiles[idx(x-1, y)]
				if (s.kind < t.kind) then
					table.insert(s.blends, {img = tileset[t.kind][2], kind = t.kind, tint_r = t.tint_r, tint_g = t.tint_g, tint_b = t.tint_b})
				end
			end
			if x > 1 and y < self.height then
				t = self.tiles[idx(x-1, y+1)]
				if (s.kind < t.kind) then
					table.insert(s.blends, {img = tileset[t.kind][3], kind = t.kind, tint_r = t.tint_r, tint_g = t.tint_g, tint_b = t.tint_b})
				end
			end
			if y < self.height then
				t = self.tiles[idx(x, y+1)]
				if (s.kind < t.kind) then
					table.insert(s.blends, {img = tileset[t.kind][4], kind = t.kind, tint_r = t.tint_r, tint_g = t.tint_g, tint_b = t.tint_b})
				end
			end
			if x < self.width and y < self.height then
				t = self.tiles[idx(x+1, y+1)]
				if (s.kind < t.kind) then
					table.insert(s.blends, {img = tileset[t.kind][5], kind = t.kind, tint_r = t.tint_r, tint_g = t.tint_g, tint_b = t.tint_b})
				end
			end
			
			-- sort blend order by priority
			table.sort(s.blends, function (a, b) return a.kind < b.kind end)
		end
	end
end

function tile_world:draw()
	local px = self.offset.x
	local py = self.offset.y
	local rowcount = 0
	
	local left, right, top, bottom = getCamera():getBounds()
	
	-- alter bounds to get some slop room on the sides
	left = left - tile_size
--	right = right + tile_size
	top = top + tile_size
--	bottom = bottom - tile_size
	
	for i, v in ipairs(self.tiles) do
		if px > left and px < right and py > bottom and py < top then
			v:draw(px, py)
		end
		
		px = px + tile_size
		rowcount = rowcount + 1
		
		if rowcount == self.width then
			rowcount = 0
			px = self.offset.x
			py = py - tile_size
		end
	end
end







